import { IFn } from './types'

/**
 * @description: sessionStorage/localStorage 拦截 getItem/setItem
 * @author: huen2015
 */

interface Detail {
	key: string
	value: any
}
type Storage = 'sessionStorage' | 'localStorage'
type StorageFn = 'getItem' | 'setItem'

const SessionStorage: Storage = 'sessionStorage'
const GetItem = 'getItem'

class InterceptStorage {
	private storageType: Storage
	private dep: string[]
	private depCallback: IFn
	private storageFnType: StorageFn
	private originalStorage: IFn
	private symbolEventType: string

	constructor(
		depCallback: IFn,
		dep: string[] = [],
		storageType: Storage = SessionStorage,
		storageFnType: StorageFn = GetItem,
	) {
		this.storageType = storageType
		this.dep = dep
		this.depCallback = depCallback
		this.storageFnType = storageFnType
		this.originalStorage = (window as Window)[this.storageType][
			this.storageFnType
		]
		this.symbolEventType =
			'customEvent' + Math.random().toString().slice(2, 10)
		this.init()
	}

	init = () => {
		const originalStorage = this.originalStorage
		const symbolEventType = this.symbolEventType
		const dispatchEvent = this.dispatchEvent
		const addListener = this.addListener

		;(window as Window)[this.storageType][this.storageFnType] = function (
			key: string,
		) {
			const that = this
			// 执行原始逻辑
			const value = originalStorage.apply(that, [key])
			// 自定义事件
			const event = new CustomEvent(symbolEventType, {
				detail: {
					key,
					value,
				},
				bubbles: false, //是否冒泡
				cancelable: false, //是否取消默认事件
			})
			// 监听自定义事件
			addListener()
			// 触发自定义事件
			dispatchEvent(event)
			return value
		}
	}

	handleEvent = (e: Event) => {
		const depCallback = this.depCallback
		const dep = this.dep
		const detail = (e as CustomEvent<Detail>).detail
		if (dep.length === 0 || dep.includes(detail.key)) {
			depCallback(detail)
		}
		this.removeListener()
	}

	addListener = () => {
		window.addEventListener(this.symbolEventType, this.handleEvent)
	}

	removeListener = () => {
		window.removeEventListener(this.symbolEventType, this.handleEvent)
	}

	dispatchEvent = (event: Event) => {
		window.dispatchEvent(event)
	}
}

// 功能测试
sessionStorage.setItem('key1', 'value1')
sessionStorage.setItem('key2', 'value2')
const depCallback = (detail: Detail) => {
	console.log('detail', detail)
}
const store = new InterceptStorage(depCallback)
